iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
Modern Web

30 天掌握 React & Next.js:從基礎到面試筆記系列 第 22

Day 22:Suspense & React.lazy — 從「等 JS」到「等 資料」的演進

  • 分享至 

  • xImage
  •  

你是不是也常搞混這兩句?

「用 React.lazy 就要搭 Suspense 啊~」
「Suspense 可以等資料載入嗎?」

很多人以為 Suspense 只是拿來顯示 Loading...
但在 React 18 之後,它其實變成了一個「通用等待機制」。
今天我們就一次弄清楚:

React.lazy() 到底在等什麼?
Suspense 又是怎麼幫它(或其他 Promise)處理的?


概念解釋

React.lazy() 會回傳一個會在 render 階段「丟出 Promise」的 component。
當 React 發現這個 Promise 時,會交給 <Suspense> 處理,顯示 fallback,
直到 Promise 完成,再重新 render 真正內容。

而在 React 18 的 Server Component 世界裡,
任何 async function component 也可能在 render 丟 Promise(例如 fetch 資料)。
此時 Suspense 一樣會介入,先渲染 fallback,待資料回來再更新畫面。

範例程式碼

錯誤範例:直接在 Client 等 fetch

'use client';

export default function Profile() {
  const data = fetch('/api/user').then(r => r.json()); // ❌ 無法在 render 等
  return <div>{data.name}</div>;
}

fetch() 在瀏覽器是非同步,React 無法在 render 階段等它,
因此這段程式不會被 Suspense 捕捉。

正確範例:

(A) Client:用 React.lazy 等 JS 模組

'use client';
import { lazy, Suspense } from 'react';
const Chart = lazy(() => import('./Chart'));

export default function Dashboard() {
  return (
    <Suspense fallback={<p>Loading chart...</p>}>
      <Chart />
    </Suspense>
  );
}

(B) Server:用 async component 等 資料

// app/UserList.jsx
export default async function UserList() {
  const users = await fetch('https://api.example.com/users').then(r => r.json());
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// app/page.jsx
import { Suspense } from 'react';
import UserList from './UserList';

export default function Page() {
  return (
    <Suspense fallback={<p>Loading users...</p>}>
      <UserList />
    </Suspense>
  );
}

常見錯誤 / 誤解

誤解 真相
Suspense 只能配 React.lazy React 18 開始可等任何 Promise(含 Server 資料)。
lazy 可以在 Server 用 ❌ Server 不用下載 JS,lazy 僅限 Client。
Suspense 會等 useEffect 裡的 fetch ❌ useEffect 在 render 後才跑,React 等不到。

實務應用(開發與面試)

  • 效能優化:用 React.lazy 分割大 bundle,減少初始載入時間。
  • Server Rendering:在 Next.js App Router 中,用 Suspense 讓 Server Component 等資料並 stream HTML。
  • 面試時可說:「Suspense 原本只用於 lazy 載入,現在也能協助 Server 端等待 fetch Promise,提升 UX 與 效能。」

小練習

1️⃣ 試著把一個大型 chart 或 editor 元件改寫成 React.lazy() 載入。
2️⃣ 在 Next.js App Router 中建立一個 async Server Component,
<Suspense> 包起來,觀察 fallback 在 stream 時的行為。


面試回答模板

中文

React.lazy 用來延遲載入 JS 模組,是 client-side 的 code splitting。
<Suspense> 則是 React 用來捕捉 Promise 並顯示 fallback 的機制。
在 React 18 中,Suspense 也能在 Server 等待 async component 的 fetch。
簡單說:lazy 產生 Promise,Suspense 處理 Promise。

英文

React.lazy is for client-side code splitting — it throws a Promise while loading a JS chunk.
Suspense is the mechanism that React uses to catch that Promise and show a fallback.
In React 18, Suspense can also handle async Server Components that fetch data before rendering.


總結

React.lazy() = client 端 JS 懶載入(code splitting)
<Suspense> = React 的 Promise 等待機制(server + client 通用)
✅ lazy 產生 Promise,Suspense 負責 fallback 與 重新 render。
✅ React 18 讓 Suspense 能 等待 Server 端的 資料抓取與 Streaming SSR。


上一篇
Day 21:不要讓抽象變成災難:從 DRY 到 WET 的思考
系列文
30 天掌握 React & Next.js:從基礎到面試筆記22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言